home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 051-075 / scopedisk58 / dfc2 / dfc.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  40KB  |  1,301 lines

  1. /*
  2.  *   This is dfc, a disk copying and formatting utility written by Radical
  3.  *   Eye Software.  This program is in the public domain; anyone can do
  4.  *   anything they want with it.  This software is made available on an as-is
  5.  *   basis; don't come to me if you destroy your entire fish disk library
  6.  *   with it!  Of course, it was tested rather extensively before it was
  7.  *   released . . .
  8.  *
  9.  *   Version 2 released on 4 Febuary 1989.  Supplied default `Empty' name
  10.  *   on format if no name given.  Automatically retries first cylinder to
  11.  *   avoid `errors' because of too-late diskchange.
  12.  *
  13.  *   This code was written with Manx 3.4b on February 8, 1988.
  14.  *
  15.  *   dfc is meant to replace both format and diskcopy, and be smaller than
  16.  *   either.  It is meant to work exactly the same as either, depending on
  17.  *   the command line arguments you give it.  In addition, it has several
  18.  *   additional options, such as buffering of a disk for a quick additional
  19.  *   copy, the ability to toggle verify mode on and off, and the ability to
  20.  *   write to multiple disks at the same time.  It also has a nice Intuition
  21.  *   interface.  It will not replace the `DiskCopy' or `Format' options from
  22.  *   the workbench, however; these have to work with the Workbench startup
  23.  *   message, which this program does not.
  24.  *
  25.  *   This program can be invoked with no command line options.  In this case,
  26.  *   it defaults to a diskcopy from df0: to df1: with verify on and buffering
  27.  *   on.  All keyword options can be preceded by a dash, for you Unix lovers
  28.  *   out there, and can be abbreviated to one character.  The following
  29.  *   parameters are accepted:
  30.  *
  31.  *   f[rom] disk       Use `disk' as the source drive.  The format for disk
  32.  *                     is completely free; only the numerals in the argument
  33.  *                     are looked at.
  34.  *   t[o] disks        Use `disks' as the destination drives.  Again, the
  35.  *                     format is unspecified.
  36.  *   v[erify]          Verify all writes (default).
  37.  *   nov[erify]        Turn off the verify mode.
  38.  *   b[uffer]          Use extra buffer memory.  If your system has enough
  39.  *                     memory, an entire disk will be cached in RAM.
  40.  *                     Otherwise, almost all of the system memory will be
  41.  *                     used.
  42.  *   nob[uffer]        Do not buffer; use minimum memory.  (Default.)
  43.  *   n[ame] diskname   Format the destination disk with name `diskname'.
  44.  *                     Otherwise a diskcopy is assumed.
  45.  *   d[...]            A drive argument.  Might not have a d; simple numbers
  46.  *                     work as well.  The first occurance of such an argument
  47.  *                     sets the source and destination fields; a subsequent
  48.  *                     occurance only sets the destination.
  49.  *
  50.  *   Thus,
  51.  *      dfc df0: name "Foo bar baz"
  52.  *   formats drive df0: and names the resultant disk "Foo bar baz".
  53.  *      dfc df0: df1:
  54.  *   diskcopies from df0: to df1:.
  55.  *      dfc -nov 0 123
  56.  *   diskcopies from drive 0 to drives 1, 2, and 3, with verify turned off.
  57.  *      dfc from df0: to df0:,df1:,df2:,df3:
  58.  *   diskcopies from drive df0: to all four drives.
  59.  *      dfc name Foo
  60.  *   formats drive df1 with the name "Foo".
  61.  *
  62.  *   Once the program opens its window up, you will have a bunch of gadgets.
  63.  *   The left-most column of gadgets is the source selection; here you can
  64.  *   choose either format, or one of the four drives.  The next column is
  65.  *   the destination column; choose any combination of the four drives by
  66.  *   clicking on the gadgets.  Lines will be drawn between these two columns
  67.  *   indicating the current selections.  If a drive is not available, no line
  68.  *   will be drawn to it.
  69.  *
  70.  *   The topmost gadget is the name gadget.  This is used to set the name of
  71.  *   your disk while formatting.
  72.  *
  73.  *   The `Go' gadget starts the program up; you had better have the disks in
  74.  *   the drives when you hit this gadget!  The `Again' gadget makes another
  75.  *   copy of the last disk, if buffering is on and enough buffers were
  76.  *   allocated for an entire disk.  This gadget requires all destination
  77.  *   disks to be in their drives already.
  78.  *
  79.  *   The `Retry' gadget tells the system to retry after a disk error.  The
  80.  *   `Quit' gadget tells the system to abort an operation; you can hit this
  81.  *   at an error, which aborts the current operation; you can hit this during
  82.  *   a copy or format operation, which does the same; or you can hit this
  83.  *   with the drives inactive, which exits the program.
  84.  *
  85.  *   The `Verify' gadget turns the verify mode on and off.  The `Buffer'
  86.  *   gadget turns buffering on and off.
  87.  *
  88.  *   All of these gadgets have keyboard shortcuts which are simply the first
  89.  *   letter of the gadget name.  The source gadgets can be selected with the
  90.  *   digits `0', `1', `2', or `3'; the destination gadgets with the shifted
  91.  *   versions of these keys (')', `!', `@', and `#'.)  The carriage return
  92.  *   key is a synonym for `Go', for added diskcopy and format compatibility.
  93.  *
  94.  *   Enjoy this program!  Please send any bug reports to Tomas Rokicki,
  95.  *   Box 2081, Stanford, CA  94309.
  96.  */
  97. #define TITLE "dfc 2 Radical Eye Software"
  98. /*
  99.  *   A handful of includes for good luck.
  100.  */
  101. #include "intuition/intuition.h"
  102. #include "functions.h"
  103. #include <exec/exec.h>
  104. #include <exec/execbase.h>
  105. #include <devices/trackdisk.h>
  106. #include <libraries/dosextens.h>
  107. /*
  108.  *   Some detach stuff.
  109.  */
  110. long _stack = 4000 ;
  111. long _priority = 0 ;
  112. long _BackGroundIO = 1 ;
  113. char *_procname = "dfc" ;
  114. /*
  115.  *   These are the various globals this routine uses.
  116.  */
  117. struct StandardPacket *gpacket ;   /* packet to send various things */
  118. struct IntuiMessage *message ;     /* the message we are working on */
  119. struct Gadget *gadad ;             /* address of gadget from message */
  120. struct Window *window ;            /* our window */
  121. struct MsgPort *port ;             /* I/O port for dos communication */
  122. struct IntuitionBase *IntuitionBase ;
  123.                                    /* we need Intuition */
  124. struct GfxBase *GfxBase ;          /* and graphics */
  125. int havedisk ;                     /* do we have a full disk in RAM? */
  126. int hibuf ;                        /* the highest buffer we have */
  127. long output ;                      /* can we write output? */
  128. char *buffers[81] ;                /* pointers to our buffers */
  129. struct IOExtTD *diskreq[4] ;       /* our I/O request blocks */
  130. long diskChangeCount[4] ;          /* last time disk was changed */
  131. int source = 1 ;                   /* the source disk, not a mask */
  132. int dest = 2 ;                     /* all destination drives */
  133. int verify = 1 ;                   /* verify writes? */
  134. int buffer = 0 ;                   /* are we using lots of memory? */
  135. char namebuf[32] ;                 /* this buffer holds disk names */
  136. char df0[] = "DF0:" ;              /* use only one copy of this name */
  137. char df1[] = "DF1:" ;              /* ditto */
  138. char df2[] = "DF2:" ;              /* ditto */
  139. char df3[] = "DF3:" ;              /* ditto */
  140. char *df[]={df0, df1, df2, df3};   /* for easy access to drive names */
  141. char blank[] =   "                    " ;
  142.                                    /* used to blank out the middle line */
  143. char msg[] = "Disk df  is not complete" ;
  144.                                    /* error message for a disk */
  145. char reading[] = "Reading track xx dfx" ;
  146.                                    /* message for reading disks */
  147. char writing[] = "Writing track xx dfx" ;
  148.                                    /* message for writing disks */
  149. char veriing[] = "Ver'ing track xx dfx" ;
  150.                                    /* message for ver'ing disks */
  151. char nobuf[] = "! couldn't get buffer" ;
  152.                                    /* if we can't get a buffer */
  153. /*
  154.  *   Always use topaz 80, and let's define an intuition buffer for our
  155.  *   general string writing stuff.
  156.  */
  157. struct TextAttr myfont = {(STRPTR)"topaz.font", TOPAZ_EIGHTY, 0, 0 };
  158. struct IntuiText intuitext = {1, 0, JAM2, 0, 0, &myfont} ;
  159. /*
  160.  *   Some defines setting the sizes of various things.  Play with these to
  161.  *   resize things.  Too bad string gadgets still have problems with large
  162.  *   fonts.
  163.  */
  164. #define WIDEGADG 83
  165. #define NARROWGADG 51
  166. #define STRGADG 256
  167. #define GADGHEIGHT 13
  168. #define LINE1 13
  169. #define LINE2 LINE1+11
  170. #define LINE3 LINE2+GADGHEIGHT
  171. #define LINE4 LINE3+GADGHEIGHT
  172. #define LINE5 LINE4+GADGHEIGHT
  173. #define LINE6 LINE5+GADGHEIGHT
  174. #define WINDOWHEIGHT LINE6+GADGHEIGHT+2
  175. #define COL1 4
  176. #define LINESTART COL1+NARROWGADG
  177. #define STRSTART COL1+NARROWGADG+23
  178. #define COL2 COL1+NARROWGADG+42
  179. #define LINEEND COL2-1
  180. #define COL3 COL2+NARROWGADG+10
  181. #define COL4 COL3+WIDEGADG+10
  182. #define WINDOWWIDTH COL4+WIDEGADG+4
  183. #define TRACKSIZE (2*512*11)
  184. /*
  185.  *   Now, some defines to assist in declaring a few things, and
  186.  *   centering strings, and the like.  These macros make the Intuition
  187.  *   crap a lot easier to put together.
  188.  */
  189. #define makeintuitext(name,string,size) struct IntuiText name={1,0,JAM2,\
  190.    (1+size-8*(sizeof(string)-1))/2,3,&myfont,(UBYTE*)string}
  191. #define makebox(name,tname,width,height,off) short tname[]={0,0,width,0,\
  192.    width,1,0,1,0,height-1,width,height-1,width,height,0,height,width,height,\
  193.    width,0};\
  194.    struct Border name={off,off,1,0,JAM2,10,tname}
  195. #define makengadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
  196.    xpos,ypos,NARROWGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
  197.    (APTR)&narrowbox,NULL,&text,NULL,NULL,ch}
  198. #define makewgadg(name,prev,xpos,ypos,text,ch) struct Gadget name={prev,\
  199.    xpos,ypos,WIDEGADG,GADGHEIGHT,GADGHCOMP,RELVERIFY,BOOLGADGET,\
  200.    (APTR)&widebox,NULL,&text,NULL,NULL,ch}
  201. /*
  202.  *   Now we declare all of our structures with the above macros.
  203.  *   First, the text for our gadgets, and then the gadgets.  No sweat.
  204.  */
  205. makeintuitext(formattext,"Format",NARROWGADG);
  206. makeintuitext(df0text,df0,NARROWGADG);
  207. makeintuitext(df1text,df1,NARROWGADG);
  208. makeintuitext(df2text,df2,NARROWGADG);
  209. makeintuitext(df3text,df3,NARROWGADG);
  210. makeintuitext(againtext,"Again",WIDEGADG);
  211. makeintuitext(retrytext,"Retry",WIDEGADG);
  212. makeintuitext(quittext,"Quit",WIDEGADG);
  213. makeintuitext(verifyontext,"Verify On ",WIDEGADG);
  214. makeintuitext(verifyofftext,"Verify Off",WIDEGADG);
  215. makeintuitext(bufferontext,"Buffer On ",WIDEGADG);
  216. makeintuitext(bufferofftext,"Buffer Off",WIDEGADG);
  217. makeintuitext(gotext,"Go",WIDEGADG);
  218. makebox(narrowbox,tn1,NARROWGADG-1,GADGHEIGHT-1,0);
  219. makebox(widebox,tn2,WIDEGADG-1,GADGHEIGHT-1,0);
  220. makebox(strbox,tn3,STRGADG-1,GADGHEIGHT-1,-2);
  221. makengadg(formatgadg,NULL,COL1,LINE2,formattext,'f');
  222. makengadg(sdf0gadg,&formatgadg,COL1,LINE3,df0text,'0');
  223. makengadg(sdf1gadg,&sdf0gadg,COL1,LINE4,df1text,'1');
  224. makengadg(sdf2gadg,&sdf1gadg,COL1,LINE5,df2text,'2');
  225. makengadg(sdf3gadg,&sdf2gadg,COL1,LINE6,df3text,'3');
  226. makengadg(ddf0gadg,&sdf3gadg,COL2,LINE3,df0text,')');
  227. makengadg(ddf1gadg,&ddf0gadg,COL2,LINE4,df1text,'!');
  228. makengadg(ddf2gadg,&ddf1gadg,COL2,LINE5,df2text,'@');
  229. makengadg(ddf3gadg,&ddf2gadg,COL2,LINE6,df3text,'#');
  230. makewgadg(gogadg,&ddf3gadg,COL3,LINE4,gotext,'g');
  231. makewgadg(retrygadg,&gogadg,COL3,LINE5,retrytext,'r');
  232. makewgadg(verifygadg,&retrygadg,COL3,LINE6,verifyontext,'v');
  233. makewgadg(againgadg,&verifygadg,COL4,LINE4,againtext,'a');
  234. makewgadg(quitgadg,&againgadg,COL4,LINE5,quittext,'q');
  235. makewgadg(buffergadg,&quitgadg,COL4,LINE6,bufferontext,'b');
  236. /*
  237.  *   We need one last gadget, the string gadget, and its associated
  238.  *   special info structure.
  239.  */
  240. struct StringInfo nameinfo={(UBYTE*)namebuf,NULL,0,31};
  241. struct Gadget namegadg={&buffergadg,STRSTART+2,LINE2+2,STRGADG-4,
  242.    GADGHEIGHT-4,GADGHCOMP,STRINGCENTER|RELVERIFY,STRGADGET,(APTR)&strbox,
  243.    NULL,NULL,NULL,(APTR)&nameinfo,'n'};
  244. /*
  245.  *   Now we have our window structure.  Initially not resizeable.
  246.  */
  247. struct NewWindow newwindow = {200,20,WINDOWWIDTH,WINDOWHEIGHT,0,1,
  248.    CLOSEWINDOW|VANILLAKEY|GADGETDOWN|GADGETUP,
  249.    WINDOWDEPTH|WINDOWCLOSE|WINDOWDRAG|SMART_REFRESH|ACTIVATE,
  250.    &namegadg,NULL,(UBYTE *)TITLE,NULL,NULL,-1,-1,-1,-1,WBENCHSCREEN};
  251. /*
  252.  *   Now we start coding.  This routine draws a string into the window at
  253.  *   a specific X, Y location.
  254.  */
  255. draw(s, x, y)
  256. char *s ;
  257. int x, y ;
  258. {
  259.    intuitext.IText = (UBYTE *)s ;
  260.    PrintIText(window->RPort, &intuitext, (long)x, (long)y) ;
  261. }
  262. /*
  263.  *   This routine gives us those pretty little DF0:BUSY things, which keep
  264.  *   AmigaDOS from futzing with the drives when we are playing with them.
  265.  */
  266. inhibit(d, t)
  267. int d ;
  268. long t ;
  269. {
  270.    register struct MsgPort *handler ;
  271.    register struct StandardPacket *packet = gpacket ;
  272.  
  273.    handler = (struct MsgPort *)DeviceProc(df[d]) ;
  274.    if (handler == NULL || port == NULL)
  275.       return ;
  276.    packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt) ;
  277.    packet->sp_Pkt.dp_Link = &(packet->sp_Msg) ;
  278.    packet->sp_Pkt.dp_Port = port ;
  279.    packet->sp_Pkt.dp_Type = ACTION_INHIBIT ;
  280.    packet->sp_Pkt.dp_Arg1 = t ;
  281.    PutMsg(handler, packet) ;
  282.    WaitPort(port) ;
  283.    GetMsg(port) ;
  284. }
  285. /*
  286.  *   This routine looks at the global message and returns a character
  287.  *   indicating the selected gadget.  This gives us easy equivalence
  288.  *   of gadgets to vanillakeys, for instance.  This also replymsg()'s
  289.  *   the message.
  290.  */
  291. int getop() {
  292.    register long class ;
  293.    register int op ;
  294.    short code ;
  295.  
  296.    class = message->Class ;
  297.    code = message->Code ;
  298.    op = ' ' ;
  299.    gadad = (struct Gadget *)(message->IAddress) ;
  300.    ReplyMsg(message) ;
  301.    message = NULL ;
  302.    if (class == CLOSEWINDOW) {
  303.       op = 'q' ;
  304.    } else if (class == GADGETDOWN || class == GADGETUP) {
  305.       op = gadad->GadgetID ;
  306.    } else if (class == VANILLAKEY) {
  307.       op = code ;
  308.       gadad = NULL ;
  309.    }
  310.    return(upcase(op)) ;
  311. }
  312. /*
  313.  *   We don't want to queue up messages, because the user might hit
  314.  *   'g' 100 times accidentally.  We flush all pending messages, and
  315.  *   return the last operation.  This way, this routine can be used
  316.  *   to see if the user typed 'Q' to exit some operation.
  317.  */
  318. int disposemsgs() {
  319.    register int op = 0 ;
  320.  
  321.    while (message = (struct IntuiMessage *)
  322.                         GetMsg(window->UserPort))
  323.       op = getop() ;
  324.    return(op) ;
  325. }
  326. /*
  327.  *   This is put into a function to help make the program smaller.
  328.  *   It takes a character and makes sure it is uppercase.
  329.  */
  330. int upcase(c)
  331. register int c ;
  332. {
  333.    if ('a' <= c && c <= 'z')
  334.       return(c-32) ;
  335.    else
  336.       return(c) ;
  337. }
  338. /*
  339.  *   Here we wait for a quit or retry key.  We also accept go, space,
  340.  *   and carriage return.
  341.  */
  342. int abortretry() {
  343.    register int op ;
  344.  
  345.    disposemsgs() ;
  346.    while (1) {
  347.       while ((message = (struct IntuiMessage *)
  348.                      GetMsg(window->UserPort))==NULL)
  349.          WaitPort(window->UserPort) ;
  350.       op = getop() ;
  351.       if (op == 'Q' || op == 'R' || op == ' ' || op == 'G' || op == 10)
  352.          return(op) ;
  353.    }
  354. }
  355. /*
  356.  *   This routine pads a string out to 40 characters.  Used to write to
  357.  *   the top line of the window.
  358.  */
  359. static char ibuf[41] ;
  360. char *to40(s)
  361. register char *s ;
  362. {
  363.    register int i = 0 ;
  364.    register char *p = ibuf ;
  365.  
  366.    while (*s != 0)
  367.       p[i++] = *s++ ;
  368.    while (i < 40)
  369.       p[i++] = ' ' ;
  370.    return(p) ;
  371. }
  372. /*
  373.  *   This error routine draws a message up at the top of the screen, and
  374.  *   waits for a response.  After it gets one, it returns the response.
  375.  *   It clears the top line afterwards.
  376.  */
  377. int error(s)
  378. register char *s ;
  379. {
  380.    int op ;
  381.  
  382.    DisplayBeep(NULL) ;
  383.    if (*s == '!' || !window) {
  384.       if (output) {
  385.          Write(output, s, (long)strlen(s)) ;
  386.          Write(output, "\n", 1L) ;
  387.       }
  388.       cleanup() ;
  389.    } else {
  390.       draw(to40(s), COL1, LINE1+2) ;
  391.       op = abortretry() ;
  392.    }
  393.    draw(to40(""), COL1, LINE1+2) ;
  394.    return(op) ;
  395. }
  396. /*
  397.  *   If the user selects a set of drives, this routine first tries to
  398.  *   allocate them.  Then, based on the success of the allocation, it
  399.  *   draws lines in the window indicating the source and destination
  400.  *   drives.
  401.  */
  402. redrawlines() {
  403.    register int i, j ;
  404.  
  405.    allocdisks() ;
  406.    SetAPen(window->RPort, 0L) ;
  407.    RectFill(window->RPort, (long)LINESTART, (long)LINE2, (long)STRSTART-1,
  408.                            (long) LINE6+GADGHEIGHT) ;
  409.    RectFill(window->RPort, (long)STRSTART, (long)LINE3, (long)LINEEND,
  410.                            (long)LINE6+GADGHEIGHT) ;
  411.    SetAPen(window->RPort, 1L) ;
  412.    i = LINE3 + (GADGHEIGHT+1)/2 + source * GADGHEIGHT ;
  413.    for (j=0; j<4; j++)
  414.       if (dest & (1 << j)) {
  415.          Move(window->RPort, (long)LINESTART, (long)i) ;
  416.          Draw(window->RPort, (long)LINEEND,
  417.                          (long)(LINE3 + (GADGHEIGHT+1)/2 + j * GADGHEIGHT)) ;
  418.       }
  419. }
  420. /*
  421.  *   This routine parses a string, usually something like 'df0:' or
  422.  *   'df0:,df1:,df2:', but can be even '012', into just a mask indicating
  423.  *   which drives were selected.  It simply looks for the characters '0',
  424.  *   '1', '2', and '3'.
  425.  */
  426. int getmask(s)
  427. register char *s ;
  428. {
  429.    register int t = 0 ;
  430.  
  431.    while (*s != 0) {
  432.       if ('0' <= *s && *s <= '3')
  433.          t |= 1 << (*s - '0') ;
  434.       s++ ;
  435.    }
  436.    return(t) ;
  437. }
  438. /*
  439.  *   This is our exit routine.  It frees the drives, deletes the ports,
  440.  *   buffers, closes the window, and libraries.  Then it exits.
  441.  */
  442. cleanup() {
  443.    register int i ;
  444.  
  445.    for (i=0; i<4; i++)
  446.       FreeDisk(i) ;
  447.    if (gpacket)
  448.       FreeMem(gpacket, (long)sizeof(struct StandardPacket)) ;
  449.    if (port)
  450.       DeletePort(port) ;
  451.    freebuffers() ;
  452.    if (window)
  453.       CloseWindow(window) ;
  454.    if (GfxBase)
  455.       CloseLibrary(GfxBase) ;
  456.    if (IntuitionBase)
  457.       CloseLibrary(IntuitionBase) ;
  458.    exit(0) ;
  459. }
  460. /*
  461.  *   This routine attempts to allocate m buffers.  We try and leave at
  462.  *   least 32K of memory, even with the allocations.  We set hibuf at the
  463.  *   exit point.
  464.  */
  465. getbuffers(m)
  466. int m ;
  467. {
  468.    register int i ;
  469.  
  470.    if (buffers[80]==NULL &&
  471.       (buffers[80]=(char *)
  472.               AllocMem((long)TRACKSIZE, MEMF_CHIP | MEMF_PUBLIC))==NULL)
  473.       error(nobuf) ;
  474.    hibuf = 0 ;
  475.    for (i=0; i<m; i++)
  476.       if (buffers[i]==NULL &&
  477.          (AvailMem(MEMF_PUBLIC) < 32000 ||
  478.          (buffers[i]=(char *)AllocMem((long)TRACKSIZE, MEMF_PUBLIC))==NULL))
  479.             break ;
  480.       else
  481.          hibuf = i+1 ;
  482.    if (hibuf < 1)
  483.       error(nobuf) ;
  484.    for (; i<80; i++)
  485.       if (buffers[i] != NULL) {
  486.          FreeMem(buffers[i], (long)TRACKSIZE) ;
  487.          buffers[i] = NULL ;
  488.       }
  489. }
  490. /*
  491.  *   And this routine lets them all go.
  492.  */
  493. int freebuffers() {
  494.    register int i ;
  495.  
  496.    for (i=0; i<81; i++)
  497.       if (buffers[i] != NULL) {
  498.          FreeMem(buffers[i], (long)TRACKSIZE) ;
  499.          buffers[i] = NULL ;
  500.       }
  501. }
  502. /*
  503.  *   We attempt to create an I/O request.  We allocate memory for it, and
  504.  *   then initialize some of the ports.
  505.  */
  506. struct IORequest *CreatExtIO() {
  507.    register struct IORequest *ioReq ;
  508.  
  509.    ioReq = (struct IORequest *)AllocMem((long)sizeof(struct IOExtTD),
  510.                                         MEMF_CLEAR | MEMF_PUBLIC) ;
  511.    if (ioReq == NULL)
  512.       return (NULL) ;
  513.    ioReq->io_Message.mn_Node.ln_Type = NT_MESSAGE ;
  514.    ioReq->io_Message.mn_Node.ln_Pri = 0 ;
  515.    ioReq->io_Message.mn_ReplyPort = port ;
  516.    return(ioReq) ;
  517. }
  518. /*
  519.  *   This routine frees an I/O request.
  520.  */
  521. DeleteIO(ioExt)
  522. struct IORequest *ioExt ;
  523. {
  524.    FreeMem(ioExt, (long)sizeof(struct IOExtTD)) ;
  525. }
  526. /*
  527.  *   Now we try to allocate a disk.  First, we attempt to allocate an
  528.  *   I/O request, and then we actually open the device.  If either of
  529.  *   these fail, we return 0.  This can occur if a drive is opened that
  530.  *   doesn't exist.  After we have the disk, we inhibit AmigaDOS from
  531.  *   putzing with it.
  532.  */
  533. int OpenDisk(i)
  534. register int i ;
  535. {
  536.    register struct IOExtTD **p = diskreq+i ;
  537.  
  538.    if (*p)
  539.       return(1) ;
  540.    if ((*p = (struct IOExtTD *)CreatExtIO()) &&
  541.        OpenDevice(TD_NAME, (long)i, *p, 0L) == 0) {
  542.       inhibit(i, TRUE) ;
  543.       return(1) ;
  544.    } else {
  545.       if (*p) {
  546.          DeleteIO(*p) ;
  547.          *p = NULL ;
  548.       }
  549.       return(0) ;
  550.    }
  551. }
  552. /*
  553.  *   Here we release a disk.  We check that we have it first!  Then, we
  554.  *   close the device, uninhibit the drive, kill the I/O request, and
  555.  *   exit.
  556.  */
  557. FreeDisk(i)
  558. register int i ;
  559. {
  560.    register struct IOExtTD **p = diskreq+i ;
  561.  
  562.    if (*p) {
  563.       CloseDevice(*p) ;
  564.       inhibit(i, FALSE) ;
  565.       DeleteIO(*p) ;
  566.       *p = NULL ;
  567.    }
  568. }
  569. /*
  570.  *   This routine attempts to allocate all of the disks we need, based on the
  571.  *   current settings of source and dest.  If a disk cannot be allocated, it
  572.  *   is removed from both source or dest.  Source might be set to point to df0,
  573.  *   if this occurs.  Then, if we can't allocate df0, we exit fatally.  This
  574.  *   occurance can actually happen, if some other program has df0:.  (I think.)
  575.  */
  576. allocdisks() {
  577.    register int i, need ;
  578.  
  579. top:
  580.    need = dest ;
  581.    if (source != -1)
  582.       need |= 1 << source ;
  583.    for (i=0; i<4; i++, need >>= 1) {
  584.       if (need & 1) {
  585.          if (! OpenDisk(i)) {
  586.             dest &= ~(1 << i) ;
  587.             if (source == i) {
  588.                if (i == 0)
  589.                   error("! I couldn't allocate the internal drive") ;
  590.                source = 0 ;
  591.                goto top ;
  592.             }
  593.         }
  594.       } else
  595.         FreeDisk(i) ;
  596.    }
  597. }
  598. /*
  599.  *   We call this routine if we are going to be accessing this
  600.  *   disk.  It gets the changecount, so our read/write routines work.
  601.  */
  602. int InitDisk(d)
  603. register int d ;
  604. {
  605.    register struct IOExtTD *p = diskreq[d] ;
  606.    register int result ;
  607.  
  608.    p->iotd_Req.io_Command = TD_CHANGENUM ;
  609.    DoIO(p) ;
  610.    result = (diskChangeCount[d] != p->iotd_Req.io_Actual) ;
  611.    diskChangeCount[d] = p->iotd_Req.io_Actual ;
  612.    return(result) ;
  613. }
  614. /*
  615.  *   The plural of the above routine takes a mask and initializes all of
  616.  *   the drives.  It adds the source drive to the list automatically.
  617.  */
  618. initdisks(mask)
  619. register int mask ;
  620. {
  621.    register int d ;
  622.  
  623.    if (source != -1)
  624.       mask |= (1 << source) ;
  625.    for (d=0; d<4; d++)
  626.       if (mask & (1 << d))
  627.          InitDisk(d) ;
  628. }
  629. /*
  630.  *   Kill the motor of a drive.  So someone can stick disks in and out.
  631.  */
  632. motoroff(d)
  633. register int d ;
  634. {
  635.    register struct IOExtTD *p = diskreq[d] ;
  636.  
  637.    p->iotd_Req.io_Length = 0 ;
  638.    p->iotd_Req.io_Command = TD_MOTOR ;
  639.    DoIO(p) ;
  640. }
  641. /*
  642.  *   This routine is a fast machine-language block move, that moves
  643.  *   exactly one block of data.  Do not change TRACKSIZE and expect
  644.  *   this still to work!
  645.  */
  646. fcpy(dest, src)
  647. long *dest, *src ;
  648. {
  649. #asm
  650.     movem.l    a0-a6/d0-d7,-(a7)
  651.     move.l    12(a5),a0
  652.     move.l    8(a5),a1
  653.     move.l    #43,d0
  654. lsdf:
  655.     movem.l    (a0)+,a2-a6/d1-d7
  656.     movem.l    a2-a6/d1-d7,(a1)
  657.     add.w    #48,a1
  658.     movem.l    (a0)+,a2-a6/d1-d7
  659.     movem.l    a2-a6/d1-d7,(a1)
  660.     add.w    #48,a1
  661.     movem.l    (a0)+,a2-a6/d1-d7
  662.     movem.l    a2-a6/d1-d7,(a1)
  663.     add.w    #48,a1
  664.     movem.l    (a0)+,a2-a6/d1-d7
  665.     movem.l    a2-a6/d1-d7,(a1)
  666.     add.w    #48,a1
  667.     movem.l    (a0)+,a2-a6/d1-d7
  668.     movem.l    a2-a6/d1-d7,(a1)
  669.     add.w    #48,a1
  670.     movem.l    (a0)+,a2-a5
  671.     movem.l    a2-a5,(a1)
  672.     add.w    #16,a1
  673.     dbra    d0,lsdf
  674.     movem.l    (a7)+,a0-a6/d0-d7
  675. #endasm
  676. }
  677. /*
  678.  *   Another fast assembly language routine for verifying a buffer.  This
  679.  *   routine returns 0 if the two buffers are the same, and something else
  680.  *   otherwise.
  681.  */
  682. int fcmp(dest, src)
  683. long *dest, *src ;
  684. {
  685.     register int foo = 0 ;
  686. #asm
  687.     movem.l    d0/a0/a1,-(a7)
  688.     move.l    12(a5),a0
  689.     move.l    8(a5),a1
  690.     move.l    #2815,d0
  691. alsdf:
  692.     cmp.l    (a0)+,(a1)+
  693.     dbne    d0,alsdf
  694.     move.w    d0,d4
  695.     addq.w    #1,d4
  696.     movem.l    (a7)+,a0/a1/d0
  697. #endasm
  698.    return(foo) ;
  699. }
  700. /*
  701.  *   This routine turns on a particular gadget.
  702.  */
  703. turnon(g)
  704. register struct Gadget *g ;
  705. {
  706.    if (g->Flags & GADGDISABLED) {
  707.       SetAPen(window->RPort, 0L) ;
  708.       RectFill(window->RPort, (long)g->LeftEdge, (long)g->TopEdge,
  709.                               (long)g->LeftEdge+g->Width-1,
  710.                               (long)g->TopEdge+g->Height-1) ;
  711.       SetAPen(window->RPort, 1L) ;
  712.       OnGadget(g, window, NULL) ;
  713.    }
  714. }
  715. /*
  716.  *   This routine turns off a particular gadget.
  717.  */
  718. turnoff(g)
  719. register struct Gadget *g ;
  720. {
  721.    if (!(g->Flags & GADGDISABLED))
  722.       OffGadget(g, window, NULL) ;
  723. }
  724. /*
  725.  *   Our main copy routine.  The variable j holds the current destinations
  726.  *   that are being written into; as disks drop like flies, j will drop
  727.  *   them; at the end, we print a message about all the disks that dropped
  728.  *   out.  We start by initializing the disks.
  729.  */
  730. goforit(again)
  731. int again ;
  732. {
  733.    register int i, j, k, t ;
  734.    register int ohbuf ;
  735.  
  736.    turnoff(&gogadg) ;
  737.    turnoff(&verifygadg) ;
  738.    turnoff(&buffergadg) ;
  739.    turnoff(&againgadg) ;
  740.    turnon(&retrygadg) ;
  741.    turnoff(&namegadg) ;
  742.    RefreshGadgets(window->FirstGadget, window, NULL) ;
  743.    j = dest ;
  744.    initdisks(j) ;
  745.    ohbuf = hibuf ;
  746. /*
  747.  *   If we are formatting, we only use one buffer.  This avoids the
  748.  *   unsightly delay which happens if we build up the formatted disk in
  749.  *   memory first; the user wonders what the hell is going on.  We first
  750.  *   check that the user isn't trying to read and write from the same disk;
  751.  *   if he is, he will have to swap about 160 times, so we tell him no go.
  752.  */
  753.    if (source == -1)
  754.       ohbuf = 1 ;
  755.    if (ohbuf == 1 && source >= 0 && (dest & (1 << source))) {
  756.       while (error("No buffering?") == 'R') ;
  757.       goto finishup ;
  758.    }
  759.    for (t=0; t<80; t+=ohbuf) {
  760.       if (! again) {
  761.          for (k=0; k<ohbuf && t+k<80; k++) {
  762.             if (disposemsgs()=='Q')
  763.                goto aborted ;
  764.             if (getdata(source, t+k, k)==0)
  765.                goto finishup ;
  766.          }
  767.          if (source != -1) {
  768.             if (dest & (1 << source)) {
  769.                for (i=0; i<4; i++)
  770.                   if (j & (1 << i))
  771.                      motoroff(i) ;
  772.                do {
  773.                   if (error("Enter destination disks")=='Q')
  774.                      goto finishup ;
  775.                } while (! InitDisk(source)) ;
  776.                initdisks(j) ;
  777.             }
  778.          }
  779.          if (ohbuf == 80)
  780.             havedisk = 1 ;
  781.       }
  782.       for (k=0; k<ohbuf && t+k<80; k++) {
  783.          if (disposemsgs()=='Q')
  784.             goto aborted ;
  785.          for (i=0; i<4; i++) {
  786.             if (j & (1 << i)) {
  787.                if (writedata(i, t+k, k)==0) {
  788.                   j &= ~(1 << i) ;
  789.                   motoroff(i) ;
  790.                }
  791.             }
  792.          }
  793.       }
  794.       if (! again) {
  795.          if (source != -1 && (dest & (1 << source)) && t+k < 80) {
  796.             motoroff(source) ;
  797.             do {
  798.                if (error("Enter source disk")=='Q')
  799.                   goto finishup ;
  800.             } while (! InitDisk(source)) ;
  801.          }
  802.       }
  803.    }
  804.    goto finishup ;
  805. /*
  806.  *   On exit, we turn off all the motors, clear out the middle line, and
  807.  *   return.
  808.  */
  809. aborted:
  810.    j = 0 ;
  811. finishup:
  812.    for (i=0; i<4; i++)
  813.       if (diskreq[i])
  814.          motoroff(i) ;
  815.    for (i=0; i<4; i++)
  816.       if ((dest-j) & (1<<i)) {
  817.          msg[7] = i + '0' ;
  818.          error(msg) ;
  819.       }
  820.    draw(blank, COL3, LINE3+3) ;
  821.    disposemsgs() ;
  822. }
  823. /*
  824.  *   This routine writes a given message to the screen; either reading,
  825.  *   writing, or ver'ing.  It fills in the track and disk number.  It
  826.  *   checks first that the source isn't `format', which is created behind
  827.  *   the scenes instead of being read from an actual disk.  If we are
  828.  *   reading from track 40, we get the name of the disk and put it up on
  829.  *   the screen.
  830.  */
  831. writeop(s, t, d)
  832. register char *s ;
  833. register int t, d ;
  834. {
  835.    if (d != -1) {
  836.       s[14] = '0' + t / 10 ;
  837.       s[15] = '0' + t % 10 ;
  838.       s[19] = '0' + d ;
  839.       draw(s, COL3, LINE3+3) ;
  840.    }
  841. }
  842. /*
  843.  *   This routine gets data from disk d, track t, into buffer b.
  844.  *   If the disk is -1, it gets it from the format routine (later.)
  845.  *   We return 1 if success; 0 if failure.  If there is an error, we allow
  846.  *   the user to retry as many times as he likes.
  847.  */
  848. int getdata(d,t,b)
  849. register int d, t, b ;
  850. {
  851.    register struct IOExtTD *p = diskreq[d] ;
  852.    register int i = 0 ;
  853.  
  854.    if (d==-1) {
  855.       makeformatdata(t, b) ;
  856.       return(1) ;
  857.    }
  858.    do {
  859.       writeop(reading, t, d) ;
  860.       p->iotd_Req.io_Length = TRACKSIZE ;
  861.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  862.       p->iotd_Req.io_Command = ETD_READ ;
  863.       p->iotd_Count = diskChangeCount[d] ;
  864.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  865.       DoIO(p) ;
  866.       fcpy(buffers[b], buffers[80]) ;
  867.    } while (p->iotd_Req.io_Error != 0 &&
  868.             ((i=error("Read Error; Quit/Retry?"))!='Q')) ;
  869.    if (t==40 && i != 'Q')
  870.       writename(b) ;
  871.    return (i != 'Q') ;
  872. }
  873. /*
  874.  *   writedata is analagous to the above routine.  However, if verify is
  875.  *   turned on, then we read the data back in from the disk and make sure
  876.  *   that it is correct.  Note that whenever we write track 40, we first
  877.  *   update the root block creation date and last modified date.
  878.  */
  879. int writedata(d,t,b)
  880. register int d, t, b ;
  881. {
  882.    register struct IOExtTD *p = diskreq[d] ;
  883.    register int i = 0 ;
  884.    register int flag = t ;
  885.  
  886. top:
  887.    if (t==40)
  888.       updaterootblock(b) ;
  889.    do {
  890.       writeop(writing, t, d) ;
  891.       fcpy(buffers[80], buffers[b]) ;
  892.       p->iotd_Req.io_Length = TRACKSIZE ;
  893.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  894.       p->iotd_Req.io_Command = TD_FORMAT ;
  895.       p->iotd_Count = diskChangeCount[d] ;
  896.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  897.       DoIO(p) ;
  898.    } while (p->iotd_Req.io_Error != 0 && (flag++ == 0 ||
  899.             ((i=error("Write Error; Quit/Retry?"))!='Q'))) ;
  900.    if (i=='Q')
  901.       return(0) ;
  902.    if (verify) {
  903.       writeop(veriing, t, d) ;
  904.       p->iotd_Req.io_Length = TRACKSIZE ;
  905.       p->iotd_Req.io_Data = (APTR)buffers[80] ;
  906.       p->iotd_Req.io_Command = ETD_READ ;
  907.       p->iotd_Count = diskChangeCount[d] ;
  908.       p->iotd_Req.io_Offset = t * (long)TRACKSIZE ;
  909.       DoIO(p) ;
  910.       if ((p->iotd_Req.io_Error != 0 ||
  911.           fcmp(buffers[80], buffers[b])) &&
  912.           ((i=error("Verify Error; Quit/Retry?"))!='Q')) {
  913.          if (t==0)
  914.             InitDisk(d) ;
  915.          goto top ;
  916.       }
  917.    }
  918.    return (i != 'Q') ;
  919. }
  920. /*
  921.  *   This routine creates data for the format option.  Note the clever
  922.  *   way the data is built up; this routine should build a disk exactly
  923.  *   the same way the standard AmigaDOS format does.  If we are on track
  924.  *   40, some additional work must be done to create a root block and
  925.  *   bitmap, but it's not too bad.
  926.  */
  927. makeformatdata(t, b)
  928. int t, b ;
  929. {
  930.    register long *p ;
  931.    register long cs ;
  932.    register long i ;
  933.    register unsigned char *q ;
  934.  
  935.    p = (long *)buffers[b] ;
  936.    cs = 'DOS\0' + (((long)t & 48) << 16) ;
  937.    for (i=0; i<TRACKSIZE/4; i++)
  938.       *p++ = cs + (i & 3327) ;
  939.    if (t != 40)
  940.       return ;
  941.    p = (long *)buffers[b] ;
  942.    for (i=0; i<256; i++)
  943.       *p++ = 0 ;
  944.    p = (long *)buffers[b] ;
  945.    p[0] = 2 ;
  946.    p[3] = 0x48 ;
  947.    p[78] = 1 ;
  948.    p[79] = 0x371 ;
  949.    q = (unsigned char *)(p + 108) ;
  950.    if (namebuf[0]==0) {
  951.       strcpy(q+1, "Empty") ;
  952.       *q = strlen(q+1) ;
  953.       writename(b) ;
  954.    } else {
  955.       *q++ = strlen(namebuf) ;
  956.       strcpy(q, namebuf) ;
  957.    }
  958.    p[127] = 1 ;
  959.    p += 128 ;
  960.    for (i=1; i<55; i++)
  961.       p[i] = 0xffffffff ;
  962.    p[0] = 0xc000c037 ;
  963.    p[28] = 0xffff3fff ;
  964.    p[55] = 0x3fffffff ;
  965. }
  966. /*
  967.  *   This routine recalculates the checksum for a block, and updates it in
  968.  *   word 5.  The sum of all the words in a block must be 0.
  969.  */
  970. recheck(w)
  971. register long *w ;
  972. {
  973.    register int i ;
  974.    register long cs ;
  975.  
  976.    cs = 0 ;
  977.    for (i=0; i<128; i++)
  978.       cs += w[i] ;
  979.    w[5] -= cs ;
  980. }
  981. /*
  982.  *   We simply DateStamp the creation date, modification date, and
  983.  *   rechecksum the block.
  984.  */
  985. updaterootblock(b)
  986. register int b ;
  987. {
  988.    DateStamp(buffers[b] + 420) ;
  989.    DateStamp(buffers[b] + 484) ;
  990.    recheck(buffers[b]) ;
  991. }
  992. /*
  993.  *   If we read from track 40, we write the name on the screen in
  994.  *   the string gadget supplied for that purpose.
  995.  */
  996. writename(b)
  997. int b ;
  998. {
  999.    register int i = buffers[b][432] ;
  1000.    register int j ;
  1001.  
  1002.    RemoveGadget(window, &namegadg) ;
  1003.    if (i > 31)
  1004.       i = 31 ;
  1005.    for (j=0; j<i; j++)
  1006.       namebuf[j] = buffers[b][j + 433] ;
  1007.    namebuf[j] = 0 ;
  1008.    AddGadget(window, &namegadg, -1L) ;
  1009.    RefreshGadgets(&namegadg, window, NULL) ;
  1010. }
  1011. /*
  1012.  *   And finally, our main routine!  This thing is awfully long; they do
  1013.  *   get that way sometimes, don't they?
  1014.  */
  1015. main(argc, argv)
  1016. int argc ;
  1017. char *argv[] ;
  1018. {
  1019.    register int op ;
  1020.    register char *p, *q ;
  1021.    register int seen = 0 ;
  1022.  
  1023.    output = (long)Output() ;
  1024. /*
  1025.  *   First, we parse the arguments.  Arguments allowed are documented at
  1026.  *   the top of this file.  If there is a dash as the first character of
  1027.  *   an argument, we ignore it, thus allowing Unix-style options.
  1028.  */
  1029.    while (argc > 1) {
  1030.       argc-- ;
  1031.       argv++ ;
  1032.       p = *argv ;
  1033.       if (argc > 1)
  1034.          q = argv[1] ;
  1035.       else
  1036.          q = "" ;
  1037.       if (*p == '-')
  1038.          p++ ;
  1039.       switch (upcase(*p)) {
  1040. /*
  1041.  *   If the argument starts with a D, or a number, it specifies a drive.
  1042.  *   Actually, it might be a `DRIVE' keyword, which would return a mask of
  1043.  *   0, so we check the return value for 0.
  1044.  */
  1045. case '0' : case '1' : case '2' : case '3' : case 'D' :
  1046.          dest = getmask(p) ;
  1047.          if (dest != 0) {
  1048.             if (! seen)
  1049.                source = dest ;
  1050.             seen = 1 ;
  1051.          }
  1052.          break ;
  1053. /*
  1054.  *   The from keyword sets the source . . .
  1055.  */
  1056. case 'F' :
  1057.          argc-- ;
  1058.          argv++ ;
  1059.          source = getmask(q) ;
  1060.          seen = 1 ;
  1061.          break ;
  1062. /*
  1063.  *   The to keyword sets the destination . . .
  1064.  */
  1065. case 'T' :
  1066.          argc-- ;
  1067.          argv++ ;
  1068.          dest = getmask(q) ;
  1069.          break ;
  1070. /*
  1071.  *   This could either be a `nobuffer', `noverify', or `name' keyword.
  1072.  *   We check for any of these.
  1073.  */
  1074. case 'N' :
  1075.          if (p[1]=='o' || p[1]=='O') {
  1076.             if (p[2]=='v' || p[2]=='V')
  1077.                verify = 0 ;
  1078.             else if (p[2]=='b' || p[2]=='B')
  1079.                buffer = 0 ;
  1080.             else goto errorarg ;
  1081.          } else {
  1082.             if (strlen(q) > 30)
  1083.                q[30] = 0 ;
  1084.             strcpy(namebuf, q) ;
  1085.             source = 0 ;
  1086.             argc-- ;
  1087.             argv++ ;
  1088.          }
  1089.          break ;
  1090. /*
  1091.  *   Verify keyword is easy
  1092.  */
  1093. case 'V' :
  1094.          verify = 1 ;
  1095.          break ;
  1096. /*
  1097.  *   As is the buffer keyword.
  1098.  */
  1099. case 'B' :
  1100.          buffer = 1 ;
  1101.          break ;
  1102. /*
  1103.  *   We go ahead and print an error message if we didn't understand an
  1104.  *   option.
  1105.  */
  1106. default:
  1107. errorarg:
  1108.          if (output) {
  1109.             Write(output, "Unknown option ", 15L) ;
  1110.             Write(output, p, (long)strlen(p)) ;
  1111.             Write(output, "\n", 1L) ;
  1112.          }
  1113.       }
  1114.    }
  1115. /*
  1116.  *   Up to this point, the source has been a mask.  Now we turn it into an
  1117.  *   integer.
  1118.  */
  1119.    if (source & 8)
  1120.       source = 3 ;
  1121.    else if (source & 4)
  1122.       source = 2 ;
  1123.    else if (source & 2)
  1124.       source = 1 ;
  1125.    else if (source & 1)
  1126.       source = 0 ;
  1127.    else
  1128.       source = -1 ;
  1129. /*
  1130.  *   We initialize a few gadgets based on the parameters the user selected.
  1131.  */
  1132.    if (verify) {
  1133.       verifygadg.GadgetText = &verifyontext ;
  1134.    } else {
  1135.       verifygadg.GadgetText = &verifyofftext ;
  1136.    }
  1137.    if (buffer) {
  1138.       buffergadg.GadgetText = &bufferontext ;
  1139.       hibuf = 80 ;
  1140.    } else {
  1141.       buffergadg.GadgetText = &bufferofftext ;
  1142.       hibuf = 1 ;
  1143.    }
  1144. /*
  1145.  *   And now we try and open things up!  First intuition, then graphics,
  1146.  *   then our window and an I/O port.  If any of these fail, we simply
  1147.  *   exit.
  1148.  */
  1149.    if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
  1150.          "intuition.library",33L))!=NULL &&
  1151.        (GfxBase = (struct GfxBase *)OpenLibrary(
  1152.          "graphics.library",0L))!=NULL &&
  1153.          (window=OpenWindow(&newwindow))!=NULL &&
  1154.        (port=CreatePort(0L, 0L)) &&
  1155.        (gpacket=(struct StandardPacket *)AllocMem(
  1156.                (long)sizeof(struct StandardPacket),MEMF_PUBLIC|MEMF_CLEAR))) {
  1157. /*
  1158.  *   We draw the lines between what the user has selected, and enter into
  1159.  *   our main command loop.  Then, we wait for a message.  After getting one,
  1160.  *   we drop into a case statement keying off what message it was.
  1161.  */
  1162.       redrawlines() ;
  1163.       getbuffers(hibuf) ;
  1164.       while (1) {
  1165.          if (havedisk)
  1166.             turnon(&againgadg) ;
  1167.          else
  1168.             turnoff(&againgadg) ;
  1169.          turnoff(&retrygadg) ;
  1170.          turnon(&gogadg) ;
  1171.          turnon(&verifygadg) ;
  1172.          turnon(&buffergadg) ;
  1173.          if (source == -1)
  1174.             turnon(&namegadg) ;
  1175.          else
  1176.             turnoff(&namegadg) ;
  1177.          RefreshGadgets(window->FirstGadget, window, NULL) ;
  1178.          while ((message = (struct IntuiMessage *)
  1179.                         GetMsg(window->UserPort))==NULL)
  1180.             WaitPort(window->UserPort) ;
  1181.          op = getop() ;
  1182.          switch(op) {
  1183. /*
  1184.  *   A shift-0, shift-1, shift-2, or shift-3 toggles the appropriate bit
  1185.  *   in the destination, and redraws the lines.  A 0, 1, 2, or 3 sets the
  1186.  *   source to that drive, and continues.
  1187.  */
  1188. case ')' :
  1189.             dest ^= 1 ;
  1190.             goto redrawem ;
  1191. case '!' :
  1192.             dest ^= 2 ;
  1193.             goto redrawem ;
  1194. case '@' :
  1195.             dest ^= 4 ;
  1196.             goto redrawem ;
  1197. case '#' :
  1198.             dest ^= 8 ;
  1199.             goto redrawem ;
  1200. case '0' : case '1' : case '2' : case '3' :
  1201.             source = op - '0' ;
  1202. redrawem :
  1203.             redrawlines() ;
  1204.             break ;
  1205. /*
  1206.  *   If verify is selected, we toggle the state of the verify flag, and
  1207.  *   update the gadget to reflect this state.
  1208.  */
  1209. case 'V' :
  1210.             verify = ! verify ;
  1211.             RemoveGadget(window, &verifygadg) ;
  1212.             if (verify) {
  1213.                verifygadg.GadgetText = &verifyontext ;
  1214.             } else {
  1215.                verifygadg.GadgetText = &verifyofftext ;
  1216.             }
  1217.             AddGadget(window, &verifygadg, -1L) ;
  1218.             RefreshGadgets(&verifygadg, window, NULL) ;
  1219.             break ;
  1220. /*
  1221.  *   The buffer option does essentially the same thing, only for buffering.
  1222.  */
  1223. case 'B' :
  1224.             buffer = ! buffer ;
  1225.             RemoveGadget(window, &buffergadg) ;
  1226.             if (buffer) {
  1227.                buffergadg.GadgetText = &bufferontext ;
  1228.                getbuffers(80) ;
  1229.             } else {
  1230.                buffergadg.GadgetText = &bufferofftext ;
  1231.                getbuffers(1) ;
  1232.             }
  1233.             havedisk = 0 ;
  1234.             AddGadget(window, &buffergadg, -1L) ;
  1235.             RefreshGadgets(&buffergadg, window, NULL) ;
  1236.             break ;
  1237. /*
  1238.  *   If the user selects `format', then we set the source appropriately and
  1239.  *   redraw the lines.  Of course, we no longer have a disk, as when the
  1240.  *   format is executed, it will destroy buffer 0.
  1241.  */
  1242. case 'F' :
  1243.             source = -1 ;
  1244.             havedisk = 0 ;
  1245.             goto redrawem ;
  1246. /*
  1247.  *   The name gadget, if invoked from the keyboard with the `n' key,
  1248.  *   Activates the string gadget.
  1249.  */
  1250. case 'N' :
  1251.             if (gadad == NULL && source == -1) {
  1252.                ActivateGadget(&namegadg, window, NULL) ;
  1253.             }
  1254.             break ;
  1255. /*
  1256.  *   The `G' gadget, or carriage return, starts us on our merry way.
  1257.  */
  1258. case 'G' : case 10 : case 32 : case 13 :
  1259.             goforit(0) ;
  1260.             break ;
  1261. /*
  1262.  *   The `A' gadget insures that we have a disk first, otherwise it
  1263.  *   complains.
  1264.  */
  1265. case 'A' :
  1266.             if (! havedisk) {
  1267.                while (error("No disk in memory")=='R') ;
  1268.             } else {
  1269.                goforit(1) ;
  1270.             }
  1271.             break ;
  1272. /*
  1273.  *   Retry is ignored in the main command loop, as there is nothing to
  1274.  *   retry!
  1275.  */
  1276. case 'R' :
  1277. /*
  1278.  *   Quit is handled at the bottom of the loop.
  1279.  */
  1280. case 'Q' :
  1281. /*
  1282.  *   As a default, we do nothing; not even an error message.
  1283.  */
  1284. default :
  1285.             break ;
  1286.          }
  1287. /*
  1288.  *   If the last gadget selected was 'Q', we exit.
  1289.  */
  1290.          if (op == 'Q')
  1291.             break ;
  1292.       }
  1293.    } else {
  1294.       error("! couldn't open window") ;
  1295.    }
  1296. /*
  1297.  *   Release memory and exit.
  1298.  */
  1299.    cleanup() ;
  1300. }
  1301.